// OpenGL Extensions
// (c) jimon game studio

#include "CRender_OpenGL_Extensions.h"
#include "jeMemory.h"
#include "ILogManager.h"
#include "IVariables.h"

#ifdef JE_RENDER_OPENGL

namespace je
{
	namespace video
	{
		//! Constructor
		CRender_OpenGL_Extensions::CRender_OpenGL_Extensions(OpenGLWorkMode WorkMode)
			:CurrentWorkMode(WorkMode)
		{
			SetNullPointers();
		}

		//! Destructor
		CRender_OpenGL_Extensions::~CRender_OpenGL_Extensions()
		{
			SetNullPointers();
		}

		//! Set Null Pointers to all functions
		void CRender_OpenGL_Extensions::SetNullPointers()
		{
			#ifdef JE_DYNAMIC_LINKAGE_CORE_FUNCTIONAL
			glCullFace = NULL;
			glTexParameteri = NULL;
			glTexImage2D = NULL;
			glClear = NULL;
			glClearColor = NULL;
			glClearDepth = NULL;
			glDepthMask = NULL;
			glDisable = NULL;
			glEnable = NULL;
			glFlush = NULL;
			glBlendFunc = NULL;
			glDepthFunc = NULL;
			glGetError = NULL;
			glGetString = NULL;
			glViewport = NULL;
			glDrawElements = NULL;
			glCopyTexImage2D = NULL;
			glBindTexture = NULL;
			glDeleteTextures = NULL;
			glGenTextures = NULL;
			glDrawBuffer = NULL;
			glReadBuffer = NULL;
			#endif

			#ifdef JE_WIN
			wglSwapIntervalEXT = NULL;
			#endif

			#ifdef JE_LINUX
			#endif

			glGenBuffers = NULL;
			glBindBuffer = NULL;
			glBufferData = NULL;
			glDeleteBuffers = NULL;
			glMapBuffer = NULL;
			glUnmapBuffer = NULL;

			glDrawRangeElements = NULL;

			glActiveTexture = NULL;

			glTexImage2DMultisample = NULL;

			glGenFramebuffers = NULL;
			glDeleteFramebuffers = NULL;
			glBindFramebuffer = NULL;
			glGenRenderbuffers = NULL;
			glDeleteRenderbuffers = NULL;
			glBindRenderbuffer = NULL;
			glRenderbufferStorage = NULL;
			glGetRenderbufferParameteriv = NULL;
			glFramebufferTexture2D = NULL;
			glFramebufferRenderbuffer = NULL;
			glCheckFramebufferStatus = NULL;
			glGenerateMipmap = NULL;
			glDrawBuffers = NULL;
			glBlitFramebuffer = NULL;
			glRenderbufferStorageMultisample = NULL;

			glAttachShader = NULL;
			glBindAttribLocation = NULL;
			glCompileShader = NULL;
			glCreateProgram = NULL;
			glCreateShader = NULL;
			glDeleteProgram = NULL;
			glDeleteShader = NULL;
			glDetachShader = NULL;
			glDisableVertexAttribArray = NULL;
			glEnableVertexAttribArray = NULL;
			glGetAttribLocation = NULL;
			glGetProgramiv = NULL;
			glGetProgramInfoLog = NULL;
			glGetShaderiv = NULL;
			glGetShaderInfoLog = NULL;
			glGetUniformLocation = NULL;
			glLinkProgram = NULL;
			glShaderSource = NULL;
			glUseProgram = NULL;
			glUniform2f = NULL;
			glUniform3f = NULL;
			glUniform2i = NULL;
			glUniform3i = NULL;
			glUniform1fv = NULL;
			glUniform3fv = NULL;
			glUniform1iv = NULL;
			glUniformMatrix3fv = NULL;
			glUniformMatrix4fv = NULL;
			glValidateProgram = NULL;
			glVertexAttribPointer = NULL;
			glBindFragDataLocation = NULL;
		}

		#define JE_BEGIN_EXT(__SupportVar)\
			{\
				u1 SupportAll = true;\
				if(core::GetVariables()->GetVariablec8(#__SupportVar))\
				{

		#ifdef JE_WIN
			#define JE_GET_EXT_GLOBAL(__Name,__Type,__SetSupport)\
			__Name = (__Type)wglGetProcAddress(#__Name);\
			if(!__Name)\
			{\
				__Name = (__Type)wglGetProcAddress(#__Name "ARB");\
				if(!__Name)\
				{\
					__Name = (__Type)wglGetProcAddress(#__Name "EXT");\
					if(!__Name)\
					{\
						JELOG_E("OpenGL Device Dont Support " #__Name)\
						if(__SetSupport)\
							SupportAll = false;\
					}\
				}\
			}
		#endif

		#ifdef JE_LINUX
			#define JE_GET_EXT_GLOBAL(__Name,__Type,__SetSupport)\
			__Name = (__Type)glXGetProcAddress(reinterpret_cast<const GLubyte*>(#__Name));\
			if(!__Name)\
			{\
				__Name = (__Type)glXGetProcAddress(reinterpret_cast<const GLubyte*>(#__Name "ARB"));\
				if(!__Name)\
				{\
					__Name = (__Type)glXGetProcAddress(reinterpret_cast<const GLubyte*>(#__Name "EXT"));\
					if(!__Name)\
					{\
						JELOG_E("OpenGL Device Dont Support " #__Name)\
						if(__SetSupport)\
							SupportAll = false;\
					}\
				}\
			}
		#endif

		#define JE_GET_EXT(__Name,__Type) JE_GET_EXT_GLOBAL(__Name,__Type,true)
		#define JE_GET_EXT_A(__Name,__Type) JE_GET_EXT_GLOBAL(__Name,__Type,false)

		#define JE_END_EXT(__SupportVar)\
				}\
				if(!SupportAll)\
					core::GetVariables()->SetVariable(#__SupportVar,0);\
			}

		#ifdef JE_DYNAMIC_LINKAGE_CORE_FUNCTIONAL
		#ifdef JE_WIN
			#define JE_BEGIN_CORE {
			#define JE_END_CORE }
		
			// HUH, with damn wglGetProcAddress cant recieve standart functionality
			#define JE_GET_CORE(__Name,__Type)\
			__Name = (__Type)GetProcAddress(GetModuleHandle(TEXT("opengl32")),#__Name);\
			if(!__Name)\
				JELOG_E("OpenGL Device Dont Support Core " #__Name)
		#endif
		#endif


		//! Init OpenGL Extensions
		void CRender_OpenGL_Extensions::GetPointers()
		{
			SetNullPointers();

			#ifdef JE_DYNAMIC_LINKAGE_CORE_FUNCTIONAL
			JE_BEGIN_CORE
			JE_GET_CORE(glCullFace,PFNGLCULLFACEPROC)
			JE_GET_CORE(glTexParameteri,PFNGLTEXPARAMETERIPROC)
			JE_GET_CORE(glTexImage2D,PFNGLTEXIMAGE2DPROC)
			JE_GET_CORE(glClear,PFNGLCLEARPROC)
			JE_GET_CORE(glClearColor,PFNGLCLEARCOLORPROC)
			JE_GET_CORE(glClearDepth,PFNGLCLEARDEPTHPROC)
			JE_GET_CORE(glDepthMask,PFNGLDEPTHMASKPROC)
			JE_GET_CORE(glDisable,PFNGLDISABLEPROC)
			JE_GET_CORE(glEnable,PFNGLENABLEPROC)
			JE_GET_CORE(glFlush,PFNGLFLUSHPROC)
			JE_GET_CORE(glBlendFunc,PFNGLBLENDFUNCPROC)
			JE_GET_CORE(glDepthFunc,PFNGLDEPTHFUNCPROC)
			JE_GET_CORE(glGetError,PFNGLGETERRORPROC)
			JE_GET_CORE(glGetString,PFNGLGETSTRINGPROC)
			JE_GET_CORE(glViewport,PFNGLVIEWPORTPROC)
			JE_GET_CORE(glDrawElements,PFNGLDRAWELEMENTSPROC)
			JE_GET_CORE(glCopyTexImage2D,PFNGLCOPYTEXIMAGE2DPROC)
			JE_GET_CORE(glBindTexture,PFNGLBINDTEXTUREPROC)
			JE_GET_CORE(glDeleteTextures,PFNGLDELETETEXTURESPROC)
			JE_GET_CORE(glGenTextures,PFNGLGENTEXTURESPROC)
			JE_GET_CORE(glDrawBuffer,PFNGLDRAWBUFFERPROC)
			JE_GET_CORE(glReadBuffer,PFNGLREADBUFFERPROC)
			JE_END_CORE
			#endif

			if(core::GetVariables()->GetVariablec8("OpenGL_LogVersion"))
			{
				JELOG(jeStringc("Driver OGL Version : '")+jeStringc(GetString(GL_VERSION))+jeStringc("'"))
				JELOG(jeStringc("Driver OGL Info : '")+jeStringc(GetString(GL_VENDOR))+jeStringc("' HW : '")+jeStringc(GetString(GL_RENDERER))+jeStringc("'"))
			}

			if(CurrentWorkMode < OWM_OPENGL3)
			{
				jeStringc Extensions = jeStringc(glGetString(GL_EXTENSIONS));

				if(core::GetVariables()->GetVariablec8("OpenGL_LogExtensions"))
					JELOG(jeStringc("Driver OGL Extensions : ")+Extensions)

				if(core::GetVariables()->GetVariablec8("OpenGL_Support_S3TC"))
				{
					u1 Finded = false;
					Extensions.Find("GL_EXT_texture_compression_s3tc",Finded);
					if(!Finded)
					{
						core::GetVariables()->SetVariable("OpenGL_Support_S3TC",0);
						JELOG_W("OpenGL Device Dont Support S3TC")
					}
				}
			}

			JE_BEGIN_EXT(OpenGL_Support_VSync)
			#ifdef JE_WIN
			JE_GET_EXT(wglSwapIntervalEXT,PFNWGLSWAPINTERVALEXTPROC)
			#endif
			JE_END_EXT(OpenGL_Support_VSync)

			JE_BEGIN_EXT(OpenGL_Support_VBO)
			JE_GET_EXT(glGenBuffers,PFNGLGENBUFFERSPROC)
			JE_GET_EXT(glBindBuffer,PFNGLBINDBUFFERPROC)
			JE_GET_EXT(glBufferData,PFNGLBUFFERDATAPROC)
			JE_GET_EXT(glDeleteBuffers,PFNGLDELETEBUFFERSPROC)
			JE_GET_EXT(glMapBuffer,PFNGLMAPBUFFERPROC)
			JE_GET_EXT(glUnmapBuffer,PFNGLUNMAPBUFFERPROC)
			JE_END_EXT(OpenGL_Support_VBO)

			JE_BEGIN_EXT(OpenGL_Support_DrawRange)
			JE_GET_EXT(glDrawRangeElements,PFNGLDRAWRANGEELEMENTSPROC)
			JE_END_EXT(OpenGL_Support_DrawRange)

			JE_BEGIN_EXT(OpenGL_Support_MultiTextures)
			JE_GET_EXT(glActiveTexture,PFNGLACTIVETEXTUREPROC)
			JE_END_EXT(OpenGL_Support_MultiTextures)

			JE_BEGIN_EXT(OpenGL_Support_AATextures)
			JE_GET_EXT(glTexImage2DMultisample,PFNGLTEXIMAGE2DMULTISAMPLEPROC)
			JE_END_EXT(OpenGL_Support_AATextures)

			JE_BEGIN_EXT(OpenGL_Support_FBO)
			JE_GET_EXT(glGenFramebuffers,PFNGLGENFRAMEBUFFERSPROC)
			JE_GET_EXT(glDeleteFramebuffers,PFNGLDELETEFRAMEBUFFERSPROC)
			JE_GET_EXT(glBindFramebuffer,PFNGLBINDFRAMEBUFFERPROC)
			JE_GET_EXT(glGenRenderbuffers,PFNGLGENRENDERBUFFERSPROC)
			JE_GET_EXT(glDeleteRenderbuffers,PFNGLDELETERENDERBUFFERSPROC)
			JE_GET_EXT(glBindRenderbuffer,PFNGLBINDRENDERBUFFERPROC)
			JE_GET_EXT(glRenderbufferStorage,PFNGLRENDERBUFFERSTORAGEPROC)
			JE_GET_EXT(glGetRenderbufferParameteriv,PFNGLGETRENDERBUFFERPARAMETERIVPROC)
			JE_GET_EXT(glFramebufferTexture2D,PFNGLFRAMEBUFFERTEXTURE2DPROC)
			JE_GET_EXT(glFramebufferRenderbuffer,PFNGLFRAMEBUFFERRENDERBUFFERPROC)
			JE_GET_EXT(glCheckFramebufferStatus,PFNGLCHECKFRAMEBUFFERSTATUSPROC)
			JE_GET_EXT(glGenerateMipmap,PFNGLGENERATEMIPMAPPROC)
			JE_GET_EXT(glDrawBuffers,PFNGLDRAWBUFFERSPROC)
			JE_GET_EXT_A(glBlitFramebuffer,PFNGLBLITFRAMEBUFFERPROC)
			JE_GET_EXT_A(glRenderbufferStorageMultisample,PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)
			JE_END_EXT(OpenGL_Support_FBO)

			JE_BEGIN_EXT(OpenGL_Support_GLSL)
			JE_GET_EXT(glAttachShader,PFNGLATTACHSHADERPROC)
			JE_GET_EXT(glBindAttribLocation,PFNGLBINDATTRIBLOCATIONPROC)
			JE_GET_EXT(glCompileShader,PFNGLCOMPILESHADERPROC)
			JE_GET_EXT(glCreateProgram,PFNGLCREATEPROGRAMPROC)
			JE_GET_EXT(glCreateShader,PFNGLCREATESHADERPROC)
			JE_GET_EXT(glDeleteProgram,PFNGLDELETEPROGRAMPROC)
			JE_GET_EXT(glDeleteShader,PFNGLDELETESHADERPROC)
			JE_GET_EXT(glDetachShader,PFNGLDETACHSHADERPROC)
			JE_GET_EXT(glDisableVertexAttribArray,PFNGLDISABLEVERTEXATTRIBARRAYPROC)
			JE_GET_EXT(glEnableVertexAttribArray,PFNGLENABLEVERTEXATTRIBARRAYPROC)
			JE_GET_EXT(glGetAttribLocation,PFNGLGETATTRIBLOCATIONPROC)
			JE_GET_EXT(glGetProgramiv,PFNGLGETPROGRAMIVPROC)
			JE_GET_EXT(glGetProgramInfoLog,PFNGLGETPROGRAMINFOLOGPROC)
			JE_GET_EXT(glGetShaderiv,PFNGLGETSHADERIVPROC)
			JE_GET_EXT(glGetShaderInfoLog,PFNGLGETSHADERINFOLOGPROC)
			JE_GET_EXT(glGetUniformLocation,PFNGLGETUNIFORMLOCATIONPROC)
			JE_GET_EXT(glLinkProgram,PFNGLLINKPROGRAMPROC)
			JE_GET_EXT(glShaderSource,PFNGLSHADERSOURCEPROC)
			JE_GET_EXT(glUseProgram,PFNGLUSEPROGRAMPROC)
			JE_GET_EXT(glUniform2f,PFNGLUNIFORM2FPROC)
			JE_GET_EXT(glUniform3f,PFNGLUNIFORM3FPROC)
			JE_GET_EXT(glUniform2i,PFNGLUNIFORM2IPROC)
			JE_GET_EXT(glUniform3i,PFNGLUNIFORM3IPROC)
			JE_GET_EXT(glUniform1fv,PFNGLUNIFORM1FVPROC)
			JE_GET_EXT(glUniform3fv,PFNGLUNIFORM1FVPROC)
			JE_GET_EXT(glUniform1iv,PFNGLUNIFORM1IVPROC)
			JE_GET_EXT(glUniformMatrix3fv,PFNGLUNIFORMMATRIX3FVPROC)
			JE_GET_EXT(glUniformMatrix4fv,PFNGLUNIFORMMATRIX4FVPROC)
			JE_GET_EXT(glValidateProgram,PFNGLVALIDATEPROGRAMPROC)
			JE_GET_EXT(glVertexAttribPointer,PFNGLVERTEXATTRIBPOINTERPROC)
			JE_GET_EXT_A(glBindFragDataLocation,PFNGLBINDFRAGDATALOCATIONPROC)
			JE_END_EXT(OpenGL_Support_GLSL)
		}
	}
}

#ifndef JE_RENDER_OPENGL_DISABLE_OLD_FUNCTIONAL

#include <GL/gl.h>

namespace je
{
	namespace video
	{
		// --------------------------- Old Functions ---------------------------
		// we cannot directly include gl.h to OpenGL Render, and cannot call opengl functions directly, so old functions for opengl 1.1 go here
			
		//! Projection Matrix Mode
		void CRender_OpenGL_Extensions::Old_MatrixModeProjection()
		{
			JE_OPENGL_CORE(glMatrixMode,(GL_PROJECTION))
		}

		//! Model View Matrix Mode
		void CRender_OpenGL_Extensions::Old_MatrixModeModelView()
		{
			JE_OPENGL_CORE(glMatrixMode,(GL_MODELVIEW))
		}

		//! Texture Matrix Mode
		void CRender_OpenGL_Extensions::Old_MatrixModeTexture()
		{
			JE_OPENGL_CORE(glMatrixMode,(GL_TEXTURE))
		}

		//! Load Matrix Values
		void CRender_OpenGL_Extensions::Old_LoadMatrixValues(f32 * Elem)
		{
			JE_OPENGL_CORE(glLoadMatrixf,(Elem))
		}

		//! Enable Client State Vertex Array
		void CRender_OpenGL_Extensions::Old_EnableClientStateVertexArray()
		{
			JE_OPENGL_CORE(glEnableClientState,(GL_VERTEX_ARRAY))
		}

		//! Enable Client State Texture Coordination Array
		void CRender_OpenGL_Extensions::Old_EnableClientStateTextureCoordinationArray()
		{
			JE_OPENGL_CORE(glEnableClientState,(GL_TEXTURE_COORD_ARRAY))
		}

		//! Enable Client State Color Array
		void CRender_OpenGL_Extensions::Old_EnableClientStateColorArray()
		{
			JE_OPENGL_CORE(glEnableClientState,(GL_COLOR_ARRAY))
		}

		//! Disable Client State Vertex Array
		void CRender_OpenGL_Extensions::Old_DisableClientStateVertexArray()
		{
			JE_OPENGL_CORE(glDisableClientState,(GL_VERTEX_ARRAY))
		}

		//! Disable Client State Texture Coordination Array
		void CRender_OpenGL_Extensions::Old_DisableClientStateTextureCoordinationArray()
		{
			JE_OPENGL_CORE(glDisableClientState,(GL_TEXTURE_COORD_ARRAY))
		}

		//! Disable Client State Color Array
		void CRender_OpenGL_Extensions::Old_DisableClientStateColorArray()
		{
			JE_OPENGL_CORE(glDisableClientState,(GL_COLOR_ARRAY))
		}

		//! Vertex Pointer
		void CRender_OpenGL_Extensions::Old_SetVertexPointer(s32 Stride,void * Ptr)
		{
			JE_OPENGL_CORE(glVertexPointer,(3,GL_FLOAT,Stride,Ptr))
		}

		//! Texture Coordination Pointer
		void CRender_OpenGL_Extensions::Old_SetTextureCoordinationPointer(s32 Stride,void * Ptr)
		{
			JE_OPENGL_CORE(glTexCoordPointer,(2,GL_FLOAT,Stride,Ptr))
		}

		//! Color Pointer
		void CRender_OpenGL_Extensions::Old_SetColorPointer(u8 Count,u8 Size,s32 Stride,void * Ptr)
		{
			GLenum type;
			switch(Size)
			{
			case 1:
				type = GL_UNSIGNED_BYTE;
				break;
			case 4:
			default:
				type = GL_FLOAT;
				break;
			case 8:
				type = GL_DOUBLE;
				break;
			}
			JE_OPENGL_CORE(glColorPointer,(Count,type,Stride,Ptr))
		}
	}
}
#else
namespace je
{
	namespace video
	{
		//! Projection Matrix Mode
		void CRender_OpenGL_Extensions::Old_MatrixModeProjection()
		{
		}

		//! Model View Matrix Mode
		void CRender_OpenGL_Extensions::Old_MatrixModeModelView()
		{
		}

		//! Load Matrix Values
		void CRender_OpenGL_Extensions::Old_LoadMatrixValues(f32 * Elem)
		{
		}

		//! Enable Client State Vertex Array
		void CRender_OpenGL_Extensions::Old_EnableClientStateVertexArray()
		{
		}

		//! Enable Client State Texture Coordination Array
		void CRender_OpenGL_Extensions::Old_EnableClientStateTextureCoordinationArray()
		{
		}

		//! Disable Client State Vertex Array
		void CRender_OpenGL_Extensions::Old_DisableClientStateVertexArray()
		{
		}

		//! Disable Client State Texture Coordination Array
		void CRender_OpenGL_Extensions::Old_DisableClientStateTextureCoordinationArray()
		{
		}

		//! Vertex Pointer
		void CRender_OpenGL_Extensions::Old_SetVertexPointer(s32 Stride,void * Ptr)
		{
		}

		//! Texture Coordination Pointer
		void CRender_OpenGL_Extensions::Old_SetTextureCoordinationPointer(s32 Stride,void * Ptr)
		{
		}
	}
}
#endif

#endif
